export interface TouchEnterLeaveInterface {
  selector: string;
  callback: Function;
}
/**
 * 用法
 */
/* const touchEnterLeave = new TouchEnterLeave('.your-item');
touchEnterLeave.onTouchEnter((element: HTMLElement) => {
  element.classList.add('rollover');
});
touchEnterLeave.onTouchLeave((element: HTMLElement) => {
  element.classList.remove('rollover');
}); */

export class TouchEnterLeave {
  private touchMoveEventListener: EventListener;

  private onTouchLeaveEvents: TouchEnterLeaveInterface[] = [];
  private onTouchEnterEvents: TouchEnterLeaveInterface[] = [];

  private lastTouchLeave?: Element;
  private lastTouchEnter?: Element;

  constructor(readonly selector: string) {
    this.touchMoveEventListener = (): void => this.touchMove();

    this.registerEvents();
  }

  onTouchEnter(callback: (element: HTMLElement) => void): () => void {
    this.onTouchEnterEvents.push({
      selector: this.selector,
      callback: callback,
    });

    return () => {
      this.onTouchEnterEvents.slice().map((item, index) => {
        if (item.selector === this.selector && item.callback === callback) {
          this.onTouchEnterEvents.splice(1, index);
        }
      });
    };
  }

  onTouchLeave(callback: (element: HTMLElement) => void): () => void {
    this.onTouchLeaveEvents.push({
      selector: this.selector,
      callback: callback,
    });

    return () => {
      this.onTouchLeaveEvents.slice().map((item, index) => {
        if (item.selector === this.selector && item.callback === callback) {
          this.onTouchLeaveEvents.splice(1, index);
        }
      });
    };
  }

  touchMove(): void {
    if (!event) return;

    const element = document.elementFromPoint(
      (event as TouchEvent).touches[0].clientX,
      (event as TouchEvent).touches[0].clientY,
    );

    if (!element) return;

    this.onTouchLeaveEvents.map((item) => {
      if (
        element !== this.lastTouchEnter &&
        this.lastTouchEnter &&
        this.lastTouchEnter.matches(item.selector)
      ) {
        if (this.lastTouchEnter !== this.lastTouchLeave) {
          item.callback(this.lastTouchEnter, event);
          this.lastTouchLeave = this.lastTouchEnter;
          this.lastTouchEnter = undefined;
        }
      }
    });

    this.onTouchEnterEvents.map((item) => {
      if (element?.matches(item.selector) && element !== this.lastTouchEnter) {
        this.lastTouchEnter = element;
        this.lastTouchLeave = undefined;

        item.callback(element, event);
      }
    });
  }

  registerEvents(): void {
    document.addEventListener('touchmove', this.touchMoveEventListener);
  }

  destroy(): void {
    document.removeEventListener('touchmove', this.touchMoveEventListener);
  }
}
